package com.tools.common.container;

import com.tools.common.container.r.OnlyReadArrayList;
import com.tools.common.container.r.OnlyReadHashSet;
import com.tools.common.container.r.OnlyReadLinkedSet;
import com.tools.common.container.r.OnlyReadTreeSet;
import com.tools.common.exception.InvalidOperationException;
import com.tools.common.object.Note;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * 自定义 Set 集合容器的
 * 抽象类实现，对 OwnRow 接口
 * 的方法做了默认实现，且兼容和扩展了
 * 原生 Set 集合的 API，使该自定义
 * 的 Set 容器被继承的子类的代码结构
 * 更加干净整洁
 * */
@Note("自定义 Set 集合容器的抽象类实现，对 OwnRow 接口" +
        "的方法做了默认实现，且兼容和扩展了原生 Set 集合的 API，" +
        "使该自定义的 Set 容器被继承的子类的代码结构更加干净整洁")
public abstract class AbsOwnSet<T> implements OwnRows<T> {

    @Note("数据存放的容器实体，使用 Set 集合做包装")
    private Set<T> body;

    @Note("针对 body 属性，使用它的哪个实现类实现它，默认是 HashSet 集合")
    private SetEnum implEnum = SetEnum.HASH;

    @Note("如果 body 不为空，是否需要对数据内容做排序，这里用于保存排序的比较器")
    private Comparator<? super T> comparator;

    /* *******************************************************************************************
     *
     *          构造器
     *
     * *******************************************************************************************
     * */

    public AbsOwnSet() {
        this.body = this.implEnum.newSet();
    }

    public AbsOwnSet(int initSize) {
        this.body = this.implEnum.newSet(initSize);
    }

    public AbsOwnSet(Comparator<? super T> c) {
        this.implEnum = SetEnum.TREE;
        this.comparator = c;
        this.body = new TreeSet<>(c);
    }


    public AbsOwnSet(T[] array) {
        private_initBody(array, null, false, this.implEnum);
    }

    public AbsOwnSet(Comparator<? super T> c, T[] array) {
        this.implEnum = SetEnum.TREE;
        this.comparator = c;
        private_initBody(array, c, true, this.implEnum);
    }


    public AbsOwnSet(Iterable<? extends T> iterable) {
        private_initBody(iterable, null, false, this.implEnum);
    }


    public AbsOwnSet(Iterable<? extends T> iterable, Comparator<? super T> c) {
        this.implEnum = SetEnum.TREE;
        this.comparator = c;
        private_initBody(iterable, c, true, this.implEnum);
    }


    public AbsOwnSet(SetEnum implEnum, int initSize) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        this.body = this.implEnum.newSet(initSize);
    }

    public AbsOwnSet(SetEnum implEnum, Comparator<? super T> c) {
        private_implNotNull(implEnum);
        this.implEnum = implEnum;
        this.comparator = c;
        this.body = this.implEnum.newSet(c);
    }


    public AbsOwnSet(SetEnum implEnum, T[] array) {
        private_implNotNull(implEnum);
        private_initBody(array, null, false, implEnum);
    }


    public AbsOwnSet(SetEnum implEnum, Comparator<? super T> c, T[] array) {
        private_implNotNull(implEnum);
        private_initBody(array, c, true, implEnum);
        this.comparator = c;
    }


    public AbsOwnSet(SetEnum implEnum, Iterable<? extends T> iterable) {
        private_implNotNull(implEnum);
        private_initBody(iterable, null,false, implEnum);
    }

    public AbsOwnSet(SetEnum implEnum, Iterable<? extends T> iterable, Comparator<? super T> c) {
        private_implNotNull(implEnum);
        private_initBody(iterable, c,true, implEnum);
        this.comparator = c;
    }

    /* *******************************************************************************************
     *
     *          链式增删改方法
     *
     * *******************************************************************************************
     * */

    @Override
    public AbsOwnSet<T> add(T obj) {
        this.body.add(obj);
        return this;
    }

    @Override
    public AbsOwnSet<T> addAll(T[] array) {
        if(array != null && array.length != 0) {
            Collections.addAll(body, array);
        }
        return this;
    }


    @Override
    public AbsOwnSet<T> addAll(Iterable<? extends T> iterable) {
        if(iterable != null) {
            for (T obj : iterable) body.add(obj);
        }
        return this;
    }


    @Override
    public AbsOwnSet<T> remove(Object obj) {
        this.body.remove(obj);
        return this;
    }


    @Override
    public AbsOwnSet<T> removeAll(Object[] array) {
        if(array == null ||  array.length == 0) return this;
        for(Object o : array) this.body.remove(o);
        return this;
    }

    @Override
    public AbsOwnSet<T> removeAll(Iterable<?> iterable) {
        if(iterable == null) return this;
        for (Object o : iterable) {
            this.body.remove(o);
        }
        return this;
    }


    /* *******************************************************************************************
     *
     *          接口方法
     *
     * *******************************************************************************************
     * */


    @Override
    public T get(int index) {
        private_checkIndex(index);
        int i = 0;
        for(T obj : body) {
            if(i == index) return obj;
            i++;
        }
        return null;
    }

    @Override
    public T getOrDefault(int index, T defaultValue) {
        return (index < 0 || index >= body.size()) ? defaultValue : this.get(index);
    }

    @Override
    public int size() {
        return body.size();
    }


    @Override
    public String toString() {
        return body.toString();
    }

    @Override
    public int hashCode() {
        return body.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if(o == this) return true;
        if(o instanceof AbsOwnSet) {
            return this.body.equals(((AbsOwnSet<?>) o).body);
        }
        return false;
    }


    @Override
    public Collection<T> toCollection() {
        return this.implEnum.newSet(body);
    }

    @Override
    public List<T> toList(ListEnum listEnum) {
        return (listEnum == null)
                ? new ArrayList<>(body)
                : listEnum.newList(body);
    }

    @Override
    public Set<T> toSet(SetEnum setEnum) {
        return (setEnum == null)
                ? new HashSet<>(body)
                : setEnum.newSet(body);
    }

    @Override
    public void copyToCollection(Collection<T> target) {
        if(target == null) return;
        target.addAll(body);
    }

    @Override
    public T[] toArray(Class<T> valueType) {
        int length = body.size();
        Object target = Array.newInstance(valueType, length);
        int index = 0;
        for(T obj : body) {
            if(index < length) {
                Array.set(target, index, obj);
                index++;
            }
        }
        return (T[]) target;
    }

    @Override
    public Object[] toArray() {
        return body.toArray();
    }

    @Override
    public OnlyReadArrayList<T> toOnlyReadArrayList() {
        return new OnlyReadArrayList<>(this.body);
    }

    @Override
    public OnlyReadHashSet<T> toOnlyReadHashSet() {
        return new OnlyReadHashSet<>(this.body);
    }

    @Override
    public OnlyReadLinkedSet<T> toOnlyReadLinkedSet() {
        return new OnlyReadLinkedSet<>(this.body);
    }

    @Override
    public OnlyReadTreeSet<T> toOnlyReadTreeSet() {
        return new OnlyReadTreeSet<>(comparator, this.body);
    }

    @Override
    public OnlyReadTreeSet<T> toOnlyReadTreeSet(Comparator<? super T> comparator) {
        return new OnlyReadTreeSet<>(comparator, this.body);
    }

    @Override
    public T random() {
        if(body.isEmpty()) return null;
        int index = ThreadLocalRandom.current().nextInt(body.size());
        return this.get(index);
    }

    @Override
    public void forEach(Consumer<? super T> action) {
        body.forEach(action);
    }

    @Override
    public Spliterator<T> spliterator() {
        return body.spliterator();
    }

    @Override
    public Iterator<T> iterator() {
        return body.iterator();
    }

    @Override
    public Comparator<? super T> getComparator() {
        return comparator;
    }

    @Override
    public void clear() {
        this.body.clear();
    }


    @Override
    public boolean contains(T obj) {
        return body.contains(obj);
    }


    @Override
    public final boolean containsAll(Object... array) {
        if(array == null || array.length == 0) return false;
        for(Object o : array) if(!body.contains(o)) return false;
        return true;
    }

    @Override
    public final boolean containsAll(Collection<?> objs) {
        if(objs == null || objs.isEmpty()) return false;
        for(Object o : objs) if(!body.contains(o)) return false;
        return true;
    }

    @Override
    public final boolean containsAll(OwnRows<?> objs) {
        if(objs == null || objs.isEmpty()) return false;
        for(Object o : objs) if(!body.contains(o)) return false;
        return true;
    }

    @Override
    public boolean containsAll(Iterable<?> iterable) {
        if(iterable != null) {
            Iterator<?> iterator = iterable.iterator();
            if(!iterator.hasNext()) return false;
            while (iterator.hasNext()) {
                if(!body.contains(iterator.next())) return false;
            }
            return true;
        }
        return false;
    }


    @Override
    public final boolean containsAny(Object... array) {
        if(array == null || array.length == 0) return false;
        for(Object o : array) if(body.contains(o)) return true;
        return false;
    }


    @Override
    public final boolean containsAny(Collection<?> objs) {
        if(objs == null || objs.isEmpty()) return false;
        for(Object o : objs) if(body.contains(o)) return true;
        return false;
    }

    @Override
    public final boolean containsAny(OwnRows<?> objs) {
        if(objs == null || objs.isEmpty()) return false;
        for(Object o : objs) if(body.contains(o)) return true;
        return false;
    }


    @Override
    public boolean containsAny(Iterable<?> iterable) {
        if(iterable != null) {
            Iterator<?> iterator = iterable.iterator();
            if(!iterator.hasNext()) return false;
            while (iterator.hasNext()) {
                if(body.contains(iterator.next())) return true;
            }
        }
        return false;
    }

    @Override
    public void sort(Comparator<? super T> c) {
        this.implEnum = SetEnum.TREE;
        this.comparator = c;
        this.body = this.implEnum.newSet(body, comparator);
    }

    @Override
    public boolean isEmpty() {
        return body == null || body.isEmpty();
    }

    /* *******************************************************************************************
     *
     *          自定义方法
     *
     * *******************************************************************************************
     * */

    public SetEnum getImplEnum() {
        return implEnum;
    }

    public Set<T> toSet() {
        return this.implEnum.newSet(body);
    }

    public T getFirst() {
        if(body.isEmpty()) return null;
        return body.iterator().next();
    }

    public T getLast() {
        if(body.isEmpty()) return null;
        T target = null;
        for(T obj : body) target = obj;
        return target;
    }


    public Stream<T> stream() {
        return body.stream();
    }

    public Stream<T> parallelStream() {
        return body.parallelStream();
    }

    public T[] toArray(T[] a) {
        return body.toArray(a);
    }



    public TreeSet<T> sortToSet(Comparator<? super T> c) {
        return (TreeSet<T>) SetEnum.TREE.newSet(body, c);
    }


    public int indexOf(T obj) {
        int i = 0;
        for(T value : body) {
            if(Objects.equals(value, obj)) {
                return i;
            }
            i++;
        }
        return -1;
    }


    public int lastIndexOf(T obj) {
        int i = 0;
        int result = -1;
        for(T value : body) {
            if(Objects.equals(value, obj)) result = i;
            i++;
        }
        return result;
    }


    public boolean removeIf(Predicate<? super T> filter) {
        return body.removeIf(filter);
    }

    public boolean retainAll(Collection<? extends T> c) {
        return body.retainAll(c);
    }

    public boolean retainAll(OwnRows<? extends T> row) {
        return body.retainAll(row.toCollection());
    }

    /* *******************************************************************************************
     *
     *          实现类特定方法
     *
     * *******************************************************************************************
     * */


    public Comparator<? super T> comparator() {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).comparator();
        }
        return comparator;
    }


    public T first() {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).first();
        }
        return this.getFirst();
    }

    public T last() {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).last();
        }
        return this.getLast();
    }


    public Iterator<T> descendingIterator() {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).descendingIterator();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public NavigableSet<T> descendingSet() {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).descendingSet();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    @Note("返回在这个集合中大于或者等于给定元素的最小元素，如果不存在这样的元素,返回 null")
    public T ceiling(T obj) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).ceiling(obj);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    @Note("返回在这个集合中小于或者等于给定元素的最大元素，如果不存在这样的元素,返回 null")
    public T floor(T obj) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).floor(obj);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public SortedSet<T> headSet(T toElement) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).headSet(toElement);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public NavigableSet<T> headSet(T toElement, boolean inclusive) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).headSet(toElement, inclusive);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public T higher(T obj) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).higher(obj);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public T lower(T obj) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).lower(obj);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public T pollFirst() {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).pollFirst();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    public T pollLast() {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).pollLast();
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public SortedSet<T> subSet(T fromElement, T toElement) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).subSet(fromElement, toElement);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public NavigableSet<T> subSet(T fromElement, boolean fromInclusive, T toElement, boolean toInclusive) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).subSet(fromElement, fromInclusive, toElement, toInclusive);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public SortedSet<T> tailSet(T fromElement) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).tailSet(fromElement);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }


    public NavigableSet<T> tailSet(T fromElement, boolean inclusive) {
        if(body instanceof TreeSet) {
            return ((TreeSet<T>) body).tailSet(fromElement, inclusive);
        }
        throw new InvalidOperationException("容器的实现类必须为 java.util.TreeSet " +
                "才可以调用该方法，当前容器实现类为 【" + implEnum.getImplClassname() + "】");
    }

    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("检查集合索引值的合法性，不能小于 0 和大于等于集合容量")
    private void private_checkIndex(int index) {
        if(index < 0 || index > body.size()) {
            throw new IllegalArgumentException("索引 " + index + " 越界，当前可用索引范围为 0 ~ " + index);
        }
    }

    @Note("Set 集合实现类枚举不能为 null")
    private void private_implNotNull(SetEnum impl) {
        if(impl == null) throw new IllegalArgumentException("若想指定内部容器的初始化的实现类型，则 SetEnum 枚举不能为 null");
    }

    @Note("根据 implEnum 构建 Set 集合，然后拷贝数组 array 里的元素到该 Set 集合里，" +
            "最后根据 useComparator 绝对要不要用 Comparator 排序比较器对集合内容进行排序")
    private void private_initBody(T[] array, Comparator<? super T> c, boolean useComparator, SetEnum implEnum) {
        this.implEnum = (implEnum == null) ? this.implEnum : implEnum;
        if(array == null || array.length == 0) {
            this.body = useComparator
                    ? this.implEnum.newSet(c)
                    : this.implEnum.newSet();
            return;
        }
        this.body = useComparator ? this.implEnum.newSet(c, array)
                : this.implEnum.newSet(array);
    }

    @Note("根据 implEnum 构建 Set 集合，然后拷贝可迭代容器 iterable 里的元素到该 Set 集合里，" +
            "最后根据 useComparator 绝对要不要用 Comparator 排序比较器对集合内容进行排序")
    private void private_initBody(Iterable<? extends T> iterable, Comparator<? super T> c, boolean useComparator, SetEnum implEnum) {
        this.implEnum = (implEnum == null) ? this.implEnum : implEnum;
        this.body = useComparator
                ? this.implEnum.newSet(iterable, c)
                : this.implEnum.newSet(iterable);
    }

    @Note("Java 自带序列化的，自定义 writeObject 序列化方法")
    private void writeObject(ObjectOutputStream output) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method writeObjectMethod = this.body.getClass().getDeclaredMethod("writeObject", ObjectOutputStream.class);
        writeObjectMethod.setAccessible(true);
        writeObjectMethod.invoke(this.body, output);
    }

    @Note("Java 自带序列化的，自定义 readObject 反序列化方法")
    private void readObject(ObjectInputStream input) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method readObjectMethod = this.body.getClass().getDeclaredMethod("readObject", ObjectInputStream.class);
        readObjectMethod.setAccessible(true);
        readObjectMethod.invoke(this.body, input);
    }
}
